package de.ludo.gaming;

import java.util.ArrayList;
import java.util.Arrays;

import de.ludo.entities.Game;
import de.ludo.entities.Player;

/**
 * @author Felix
 * 
 */
public class LudoIntegrationSupport {

	public Player nextPlayer(Game g) {
		PlayerType type = g.getCurrentPlayer().getType();

		if (PlayerType.PLAYER_YELLOW.ordinal() == type.ordinal()) {
			if (g.getPlayerGreen() != null) {
				return g.getPlayerGreen();
			} else if (g.getPlayerRed() != null) {
				return g.getPlayerRed();
			} else if (g.getPlayerBlue() != null) {
				return g.getPlayerBlue();
			}
		}
		if (PlayerType.PLAYER_RED.ordinal() == type.ordinal()) {
			if (g.getPlayerBlue() != null) {
				return g.getPlayerBlue();
			} else if (g.getPlayerYellow() != null) {
				return g.getPlayerYellow();
			} else if (g.getPlayerGreen() != null) {
				return g.getPlayerGreen();
			}
		}
		if (PlayerType.PLAYER_GREEN.ordinal() == type.ordinal()) {
			if (g.getPlayerRed() != null) {
				return g.getPlayerRed();
			} else if (g.getPlayerBlue() != null) {
				return g.getPlayerBlue();
			} else if (g.getPlayerYellow() != null) {
				return g.getPlayerYellow();
			}
		}
		if (PlayerType.PLAYER_BLUE.ordinal() == type.ordinal()) {
			if (g.getPlayerYellow() != null) {
				return g.getPlayerYellow();
			} else if (g.getPlayerGreen() != null) {
				return g.getPlayerGreen();
			} else if (g.getPlayerRed() != null) {
				return g.getPlayerRed();
			}
		}
		return null;
	}

	/**
	 * Checks, if the current player finished the game
	 * 
	 * @param g
	 *            game
	 * @return true Current Player has finished the game
	 */
	public boolean isGameFinished(Game g) {
		LudoPositionSupport posSup = new LudoPositionSupport();
		// Helper for the finish positions
		int[] finishHelper = posSup.getFinishPositions(g.getCurrentPlayer()
				.getType());
		// Helper for the actual character positions
		int[] characterHelper = getCharacterPositions(g);

		// sort finish könnte man aus performance Gründen rausnehmen
		Arrays.sort(finishHelper);
		Arrays.sort(characterHelper);

		// compairs both arrays
		if (Arrays.equals(finishHelper, characterHelper)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Checks, if the player has characters on base
	 * 
	 * @param g
	 *            game
	 * @return ArrayList<Integer> including charactersOnBase
	 */
	public ArrayList<Integer> charactersOnBase(Game g) {
		ArrayList<Integer> a = new ArrayList<>();
		LudoPositionSupport posSup = new LudoPositionSupport();
		// Helper for the base positions
		int[] baseHelper = posSup.getHousePositions(g.getCurrentPlayer()
				.getType());
		// Helper for the actual character positions
		int[] characterHelper = getCharacterPositions(g);

		for (int i = 0; i < baseHelper.length; i++) {
			for (int j = 0; j < characterHelper.length; j++) {
				if (baseHelper[i] == characterHelper[j]) {

					a.add(baseHelper[i]);
				}
			}
		}

		return a;
	}

	/**
	 * The method is called if the player HAS TO move a character away from his
	 * start position Unless the dice result is a 6, the move of this player is
	 * done
	 * 
	 * @param g
	 *            Game
	 * @param diceResult
	 *            int
	 * @param gameBoard
	 */

	public int[] moveCharacterFromStartPosition(Game g, int diceResult,
			int[] gameBoard) {
		PlayerType type = g.getCurrentPlayer().getType();
		LudoPositionSupport posSup = new LudoPositionSupport();
		if (PlayerType.PLAYER_YELLOW.equals(type)) {
			return move(
					posSup.getYellowStart(),
					calculateNewPosition(diceResult, posSup.getYellowStart(),
							type), gameBoard);
		} else if (PlayerType.PLAYER_RED.equals(type)) {
			return move(
					posSup.getRedStart(),
					calculateNewPosition(diceResult, posSup.getRedStart(), type),
					gameBoard);
		} else if (PlayerType.PLAYER_GREEN.equals(type)) {
			return move(
					posSup.getGreenStart(),
					calculateNewPosition(diceResult, posSup.getGreenStart(),
							type), gameBoard);
		} else if (PlayerType.PLAYER_BLUE.equals(type)) {
			return move(
					posSup.getBlueStart(),
					calculateNewPosition(diceResult, posSup.getBlueStart(),
							type), gameBoard);
		}
		return null;
	}

	// /**
	// * TODO funktioniert nicht, da immer nur die erste Spielfigur gesetzt wird
	// * The method is called, if the dice result allows the user to move a
	// * character from his base to his start position The start position has to
	// * be made free as soon as possible
	// */
	// public int[] putCharacterFromBaseToStartPosition(Game g, int[] gameBoard)
	// {
	//
	// PlayerType type = g.getCurrentPlayer().getType();
	// ArrayList<Integer> alHelper = charactersOnBase(g);
	// LudoPositionSupport posSup = new LudoPositionSupport();
	// if (PlayerType.PLAYER_YELLOW.equals(type)) {
	// return move(alHelper.get(0).intValue(), posSup.getYellowStart(),
	// gameBoard);
	// } else if (PlayerType.PLAYER_RED.equals(type)) {
	// return move(alHelper.get(0).intValue(), posSup.getRedStart(),
	// gameBoard);
	// } else if (PlayerType.PLAYER_GREEN.equals(type)) {
	// return move(alHelper.get(0).intValue(), posSup.getGreenStart(),
	// gameBoard);
	// } else if (PlayerType.PLAYER_BLUE.equals(type)) {
	// return move(alHelper.get(0).intValue(), posSup.getBlueStart(),
	// gameBoard);
	// }
	// return null;
	// }

	/**
	 * This method moves the characters from the old position, to the new
	 * position
	 * 
	 * @param currentPosition
	 * @param newPosition
	 */
	public int[] move(int currentPosition, int newPosition, int[] gameBoard) {
		// Helper to change Characters Position
		int helper = gameBoard[currentPosition];
		LudoPositionSupport posSup = new LudoPositionSupport();

		// Checks if Opponent on Filed and if necessary moves it to base
		if (opponentOnField(newPosition, gameBoard) != 0) {
			int oppHelper = gameBoard[newPosition];
			gameBoard[posSup.getToBase(oppHelper)] = oppHelper;
			gameBoard[newPosition] = 0;

			gameBoard[currentPosition] = 0;
			gameBoard[newPosition] = helper;
		} else {
			gameBoard[currentPosition] = 0;
			gameBoard[newPosition] = helper;
		}
		return gameBoard;
	}

	/**
	 * Returns the number of the character on the position
	 * 
	 * @param new position of currentPlayer
	 * @param gameBoard
	 * @return int characternumber, if 0 -> no player
	 */
	private int opponentOnField(int position, int[] gameBoard) {
		return gameBoard[position];
	}

	/**
	 * This method checks, if the start position of a player is free
	 * 
	 * @param g
	 *            game
	 * @param type
	 *            playerType
	 * @return true if the StartPosition is freee
	 */
	public boolean isStartPositionFree(Game g, PlayerType type) {
		LudoPositionSupport posSup = new LudoPositionSupport();
		int helper = posSup.getStartPos(type);

		int[] characterHelper = getCharacterPositions(g);

		for (int x : characterHelper) {
			if (x == helper) {
				return false;
			}
		}
		return true;
	}

	/**
	 * This method throws the dice
	 * 
	 * @return int result
	 */
	public int throwDice() {
		return (int) ((Math.random()) * 6 + 1);
	}

	/**
	 * This method helps to define the characters can be moved accordingly to
	 * the dice result
	 * 
	 * @param g
	 * @param diceResult
	 * @return list of actual positions of characters that can be moved
	 */
	public int[] selectPossibleCharacters(Game g, int diceResult) {
		// List of possible Characters
		ArrayList<Integer> ch = new ArrayList<>();
		// Array of current positions of Player
		int[] characters = getCharacterPositions(g);
		Player co = g.getCurrentPlayer();
		LudoPositionSupport posSup = new LudoPositionSupport();

		if (diceResult == 6 && isStartPositionFree(g, co.getType())
				&& charactersOnBase(g).size() > 0) {
			// Start position is free and the player has charactersOnBase
			// All charactersOnBase are moveable
			for (int x : charactersOnBase(g)) {
				ch.add(x);
			}
			return createArray(ch);

			// Start position is not free and the player has charactersOnBase
			// The start position is only moveable. But if there is an own
			// character on the nextPosition, you can't move
		}
		if (!isStartPositionFree(g, co.getType())
				&& charactersOnBase(g).size() > 0) {
			int characterOnStart = posSup.getStartPos(co.getType());
			int newPosition = calculateNewPosition(diceResult,
					characterOnStart, co.getType());
			if (newPositionIsFree(newPosition, co) && newPosition > 0) {
				ch.add(characterOnStart);
				return createArray(ch);
			} else {
				for (int i : characters) {
					int nextPosition = calculateNewPosition(diceResult, i,
							co.getType());
					if (i != characterOnStart
							&& newPositionIsFree(nextPosition, co)
							&& nextPosition > 0) {
						ch.add(i);
					}
				}
				return createArray(ch);
			}
		} else {
			// Helper, to get the type for the calculateNewPosition method
			PlayerType type = g.getCurrentPlayer().getType();
			for (int x : characters) {

				int newPosition = calculateNewPosition(diceResult, x, type);
				if (newPositionIsFree(newPosition, co) && newPosition > 0) {
					ch.add(x);
				}

				// Case that you are in base and don't dice a six. No charater
				// in base is possible
				if (newPosition == posSup.getStartPos(type) && diceResult != 6) {
					ch.remove(Integer.valueOf(x));
				}
			}
		}
		return createArray(ch);
	}

	// public int[] selectNextPossibleCharacters(Game g, int dice) {
	// ArrayList<Integer> nextChars = new ArrayList<>();
	// LudoPositionSupport pos = new LudoPositionSupport();
	// Player curPlayer = g.getCurrentPlayer();
	// PlayerType type = curPlayer.getType();
	// int startPos = pos.getStartPos(type);
	//
	// ArrayList<Integer> charsOnBase = charactersOnBase(g);
	//
	// // Fall 1: Dice 6, startPositionIsFree, charactersOnBase
	//
	// // Fall 2: Dice 6, startPositionIsNotFree, nextPositionIsFree,
	// // charactersOnBase
	//
	// // Fall 3: Dice 6, startPositionIsNotFree, nextPositionIsNotFree,
	// // charactersOnBase
	//
	// // Fall 4: Dice 6, startPositionIsNotFree, nextPositionIsNotFree,
	// // nextPositionIsNotFree
	//
	// // if (startIsNotFree && charOnStartCanMove)
	// // else if (startIsNotFree && charOnStartCanNotMove
	// if (!isStartPositionFree(g, type) && charsOnBase.size() > 0) {
	// int newPos = calculateNewPosition(dice, startPos, type);
	// if (newPositionIsFree(newPos, curPlayer)) {
	// nextChars.add(newPos);
	// return createArray(nextChars);
	// }
	// } else if (dice == 6 && isStartPositionFree(g, type)
	// && charsOnBase.size() > 0) {
	// for (Integer i : charsOnBase) {
	// nextChars.add(i);
	// }
	// return createArray(charsOnBase);
	// }
	//
	// return createArray(nextChars);
	//
	// }

	/**
	 * Helper method to create an Array out of an ArrayList
	 * 
	 * @param ch
	 * @return
	 */
	private int[] createArray(ArrayList<Integer> ch) {
		int[] r = new int[ch.size()];
		for (int i = 0; i < r.length; i++) {
			r[i] = ch.get(i);
		}
		return r;
	}

	/**
	 * This method checks if the own characters standing on the new position.
	 * 
	 * @param g
	 *            Game
	 * @param diceResult
	 * @param currentCharacterPosition
	 * @return true if the new Position is Free
	 */
	private boolean newPositionIsFree(int newPosition, Player currentPlayer) {

		if (currentPlayer.getCharacterOne() == newPosition) {
			return false;
		}
		if (currentPlayer.getCharacterTwo() == newPosition) {
			return false;
		}
		if (currentPlayer.getCharacterThree() == newPosition) {
			return false;
		}
		if (currentPlayer.getCharacterFour() == newPosition) {
			return false;
		}
		return true;
	}

	/**
	 * This Method calculates the new Position for the character, using the
	 * helperArrays
	 * 
	 * @param diceResult
	 * @param currentCharacterPosition
	 * @param type
	 * @return int with the new position
	 */
	public int calculateNewPosition(int diceResult,
			int currentCharacterPosition, PlayerType type) {
		LudoPositionSupport lps = new LudoPositionSupport();
		int[] housePositions = lps.getHousePositions(type);
		// If player is in base, the new position is the start field
		for (int position : housePositions) {
			if (position == currentCharacterPosition) {
				return lps.getStartPos(type);
			}
		}

		if (PlayerType.PLAYER_YELLOW.equals(type)) {

			// Iterate through helper array
			for (int i = 0; i < HelpArr.getYellow().length; i++) {

				if (HelpArr.getYellow()[i] == currentCharacterPosition) {
					if (i + diceResult < HelpArr.getYellow().length) {
						int newPosition = HelpArr.getYellow()[i + diceResult];
						return newPosition;
					} else
						return 0;
				}
			}
		} else if (PlayerType.PLAYER_RED.equals(type)) {
			// Iterate through helper array
			for (int i = 0; i < HelpArr.getRed().length; i++) {
				if (HelpArr.getRed()[i] == currentCharacterPosition) {
					if (i + diceResult < HelpArr.getRed().length) {
						int newPosition = HelpArr.getRed()[i + diceResult];
						return newPosition;
					} else
						return 0;
				}
			}
		} else if (PlayerType.PLAYER_GREEN.equals(type)) {
			// Iterate through helper array
			for (int i = 0; i < HelpArr.getGreen().length; i++) {
				if (HelpArr.getGreen()[i] == currentCharacterPosition) {
					if (i + diceResult < HelpArr.getGreen().length) {
						int newPosition = HelpArr.getGreen()[i + diceResult];
						return newPosition;
					} else
						return 0;
				}
			}
		} else if (PlayerType.PLAYER_BLUE.equals(type)) {
			// Iterate through helper array
			for (int i = 0; i < HelpArr.getBlue().length; i++) {
				if (HelpArr.getBlue()[i] == currentCharacterPosition) {
					if (i + diceResult < HelpArr.getBlue().length) {
						int newPosition = HelpArr.getBlue()[i + diceResult];
						return newPosition;
					} else
						return 0;
				}
			}
		}
		return 0;
	}

	/**
	 * This method returns the character of the player on the startposition
	 * 
	 * @return the Character on the startpoisiton, if retun is 0 -> there is no
	 *         character on startposition
	 */
	public int getCharacterOnStartPosition(Game g, PlayerType type) {
		LudoPositionSupport posSup = new LudoPositionSupport();
		int helper = posSup.getStartPos(type);

		int[] characterHelper = getCharacterPositions(g);

		for (int x : characterHelper) {
			if (x == helper) {
				return x;
			}
		}
		return 0;
	}

	/**
	 * returns the positions of the characters from the current player
	 * 
	 * @param g
	 * @return int[] with character positions
	 */
	private int[] getCharacterPositions(Game g) {
		Player cur = g.getCurrentPlayer();
		int[] helper = new int[4];

		helper[0] = cur.getCharacterOne();
		helper[1] = cur.getCharacterTwo();
		helper[2] = cur.getCharacterThree();
		helper[3] = cur.getCharacterFour();

		return helper;
	}

	/**
	 * Method initializes the gameBaord. Accordingly to the amount of players,
	 * the PlayerCharacters are set and the positions in the gameBoard are
	 * marked with characterNumbers (1-16)
	 * 
	 * @param g
	 * @return
	 */
	public int[] initGameBoard(Game g) {
		// Insgesamt exisitieren 72 Felder auf dem Spielbrett. Index 0 wird
		// nicht ausgewertet. Man beginnt erst ab 1, deshalb ist die Feldgröße
		// 73
		int[] gameBoard = new int[73];
		int amountOfPlayers = 4 - g.getRemainingGamePositions();
		LudoPositionSupport posSup = new LudoPositionSupport();

		for (int i = 1; i < 73; i++) {
			gameBoard[i] = 0;
		}
		g.getPlayerYellow().setCharacterOne(posSup.getYellowHouseOne());
		gameBoard[posSup.getYellowHouseOne()] = 1;
		g.getPlayerYellow().setCharacterTwo(posSup.getYellowHouseTwo());
		gameBoard[posSup.getYellowHouseTwo()] = 2;
		g.getPlayerYellow().setCharacterThree(posSup.getYellowHouseThree());
		gameBoard[posSup.getYellowHouseThree()] = 3;
		g.getPlayerYellow().setCharacterFour(posSup.getYellowHouseFour());
		gameBoard[posSup.getYellowHouseFour()] = 4;

		g.getPlayerRed().setCharacterOne(posSup.getRedHouseOne());
		gameBoard[posSup.getRedHouseOne()] = 5;
		g.getPlayerRed().setCharacterTwo(posSup.getRedHouseTwo());
		gameBoard[posSup.getRedHouseTwo()] = 6;
		g.getPlayerRed().setCharacterThree(posSup.getRedHouseThree());
		gameBoard[posSup.getRedHouseThree()] = 7;
		g.getPlayerRed().setCharacterFour(posSup.getRedHouseFour());
		gameBoard[posSup.getRedHouseFour()] = 8;

		if (amountOfPlayers >= 3) {
			g.getPlayerGreen().setCharacterOne(posSup.getGreenHouseOne());
			gameBoard[posSup.getGreenHouseOne()] = 9;
			g.getPlayerGreen().setCharacterTwo(posSup.getGreenHouseTwo());
			gameBoard[posSup.getGreenHouseTwo()] = 10;
			g.getPlayerGreen().setCharacterThree(posSup.getGreenHouseThree());
			gameBoard[posSup.getGreenHouseThree()] = 11;
			g.getPlayerGreen().setCharacterFour(posSup.getGreenHouseFour());
			gameBoard[posSup.getGreenHouseFour()] = 12;
			if (amountOfPlayers == 4) {
				g.getPlayerBlue().setCharacterOne(posSup.getBlueHouseOne());
				gameBoard[posSup.getBlueHouseOne()] = 13;
				g.getPlayerBlue().setCharacterTwo(posSup.getBlueHouseTwo());
				gameBoard[posSup.getBlueHouseTwo()] = 14;
				g.getPlayerBlue().setCharacterThree(posSup.getBlueHouseThree());
				gameBoard[posSup.getBlueHouseThree()] = 15;
				g.getPlayerBlue().setCharacterFour(posSup.getBlueHouseFour());
				gameBoard[posSup.getBlueHouseFour()] = 16;
			}
		}
		return gameBoard;
	}

	public boolean checkIfUserWasAccepted(Game g, int sessionId) {
		if (g.getPlayerRed() != null
				&& g.getPlayerRed().getSessionId() == sessionId) {
			return true;
		} else if (g.getPlayerGreen() != null
				&& g.getPlayerGreen().getSessionId() == sessionId) {
			return true;
		} else if (g.getPlayerBlue() != null
				&& g.getPlayerBlue().getSessionId() == sessionId) {
			return true;
		}
		return false;
	}

	/**
	 * This method calculates the remaining attempts after a new player is set
	 * as current player
	 */
	public int calculateRemainingAttempts(Game g) {
		LudoPositionSupport sup = new LudoPositionSupport();
		Player p = g.getCurrentPlayer();
		int[] board = g.getGameBoardAsIntArray();

		int filledHousePositions = 0;

		int[] house = sup.getHousePositions(p.getType());
		for (int i : house) {
			if (board[i] > 0) {
				filledHousePositions = filledHousePositions + 1;
			}
		}

		int finishFour = sup.getFinishPositions(p.getType())[3];
		int finishThree = sup.getFinishPositions(p.getType())[2];
		int finishTwo = sup.getFinishPositions(p.getType())[1];

		int attempts = 0;
		// all characters are on base
		if (filledHousePositions == 4) {
			attempts = 3;
			// 3 characters are on base, 1 is on his finish position
		} else if (filledHousePositions == 3 && board[finishFour] > 0) {
			attempts = 3;
			// 2 characters are on base, 2 are on their finish posititions
		} else if (filledHousePositions == 2 && board[finishFour] > 0
				&& board[finishThree] > 0) {
			attempts = 3;
			// 1 character is on base, 3 are in their finish positions
		} else if (filledHousePositions == 1 && board[finishFour] > 0
				&& board[finishThree] > 0 && board[finishTwo] > 0) {
			attempts = 3;
		} else {
			attempts = 1;
		}
		return attempts;
	}
}
